#include "QD3DUtilsTexExt.h"


QD3DUtilsTexExt::QD3DUtilsTexExt() : QD3DUtils ()
{

}

QD3DUtilsTexExt::QD3DUtilsTexExt (QD3DContext *pContext) : QD3DUtils (pContext)
{

}

QD3DUtilsTexExt::~QD3DUtilsTexExt ()
{

}

ID3D11Texture2D * QD3DUtilsTexExt::createTexture2DFromQImage (const QImage& image, UINT bindFlag)
{
	DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN;
	uchar *pImageData = NULL;
	UINT lineBytes = 0;
	bool isNew = _getDxTexDataFromQImage (image, &pImageData,
			&lineBytes, &format);
	CD3D11_TEXTURE2D_DESC desc (format, image.width (), image.height (), 1, 1,
			bindFlag);
	D3D11_SUBRESOURCE_DATA subsource = { pImageData, lineBytes, 0 };
	QVector< D3D11_SUBRESOURCE_DATA > subresources (1);
	subresources[0] = subsource;
	ID3D11Texture2D *pTexObj = createTexture2D (desc, subresources);
	if (isNew)
		delete [] pImageData;
	return pTexObj;
}

bool QD3DUtilsTexExt::_getDxTexDataFromQImage (const QImage& image, uchar **ppOutTexData, UINT *pOutLineBytes, DXGI_FORMAT *pOutFormat)
{
	DXGI_FORMAT &format = *pOutFormat;
	uchar *&pImageData = *ppOutTexData;
	UINT &lineBytes = *pOutLineBytes;
	bool isNew = false;

	switch (image.format ())
	{
	case QImage::Format_ARGB32:
	case QImage::Format_ARGB32_Premultiplied:
		// RGB Image
		pImageData = new uchar[image.width () * image.height () * 4];
		isNew = true;
		for (int v = 0; v < image.height (); ++v) {
			for (int u = 0; u < image.width (); ++u) {
				pImageData[(v * image.width () + u) * 4 + 0] = qRed (image.pixel (u, v));
				pImageData[(v * image.width () + u) * 4 + 1] = qGreen (image.pixel (u, v));
				pImageData[(v * image.width () + u) * 4 + 2] = qBlue (image.pixel (u, v));
				pImageData[(v * image.width () + u) * 4 + 3] = qAlpha (image.pixel (u, v));
			}
		}
		format = DXGI_FORMAT_R8G8B8A8_UNORM;
		lineBytes = image.width () * 4;
		break;
	case QImage::Format_RGB32:
	case QImage::Format_RGB888:
		// RGB Image
		pImageData = new uchar[image.width () * image.height () * 4];
		isNew = true;
		for (int v = 0; v < image.height (); ++v) {
			for (int u = 0; u < image.width (); ++u) {
				pImageData[(v * image.width () + u) * 4 + 0] = qRed (image.pixel (u, v));
				pImageData[(v * image.width () + u) * 4 + 1] = qGreen (image.pixel (u, v));
				pImageData[(v * image.width () + u) * 4 + 2] = qBlue (image.pixel (u, v));
				pImageData[(v * image.width () + u) * 4 + 3] = 255;
			}
		}
		format = DXGI_FORMAT_R8G8B8A8_UNORM;
		lineBytes = image.width () * 4;
		break;
	case QImage::Format_RGB16:
		pImageData = (uchar *)image.bits ();
		format = DXGI_FORMAT_R16_UINT;
		lineBytes = image.bytesPerLine ();
		break;
	case QImage::Format_Indexed8:
		pImageData = (uchar *)image.bits ();
		format = DXGI_FORMAT_R8_UINT;
		lineBytes = image.bytesPerLine ();
		break;
	default:
		throw std::exception ("QD3DUtilsCvExt::_getDxTexDataFromQImage(): "
				"Format not support.");
		break;
	}
	return isNew;
}

void QD3DUtilsTexExt::updateTexture2D (ID3D11Texture2D *pDstDxTex, QImage& image)
{
	DXGI_FORMAT format = DXGI_FORMAT_UNKNOWN;
	uchar *pImageData = NULL;
	UINT lineBytes = 0;
	bool isNew = _getDxTexDataFromQImage (image, &pImageData,
			&lineBytes, &format);
	updateSubresource (pDstDxTex, 0, pImageData, NULL, lineBytes);
	if (isNew)
		delete [] pImageData;
}

void QD3DUtilsTexExt::downloadTexture2D (ID3D11Texture2D *p_dxTex, QImage& image)
{
	D3D11_TEXTURE2D_DESC texDesc;
	p_dxTex->GetDesc (&texDesc);
	D3D11_MAPPED_SUBRESOURCE subResource;
	map (D3D11_MAP_READ, p_dxTex, &subResource);

	switch (texDesc.Format) {
	case DXGI_FORMAT_R8G8B8A8_UNORM:
		image = QImage (texDesc.Width, texDesc.Height, QImage::Format_ARGB32);
		for (int v = 0; v < image.height (); ++v){
			for (int u = 0; u < image.width (); ++u) {
				char* srcPixel = (char*)subResource.pData + v * subResource.RowPitch + u * 4;
				image.setPixel (u, v, qRgba (srcPixel[0], srcPixel[1], srcPixel[2], srcPixel[3]));
			}
		}
		break;
	case DXGI_FORMAT_R8_UINT:
		image = QImage (texDesc.Width, texDesc.Height, QImage::Format_Indexed8);
		for (int v = 0; v < image.height (); ++v)
			memcpy (image.scanLine (v),
					(char*)subResource.pData + v * subResource.RowPitch,
					image.bytesPerLine ());
		break;
	case DXGI_FORMAT_R16_UINT:
		image = QImage (texDesc.Width, texDesc.Height, QImage::Format_RGB16);
		for (int v = 0; v < image.height (); ++v)
			memcpy (image.scanLine (v),
					(char*)subResource.pData + v * subResource.RowPitch,
					image.bytesPerLine ());
		break;
	}
	unmap (p_dxTex);
}

void QD3DUtilsTexExt::getRenderTexture (QImage& image)
{
	ID3D11Texture2D* p_backBuffer = NULL;
	HRESULT hr = __p_context->pSwapChain->GetBuffer (0,
			__uuidof(ID3D11Texture2D), (void**)&p_backBuffer);
	if (FAILED (hr))
		throw std::exception ("QD3DUtilsCvExt::getRenderTexture"
				"Fail to get render texture object");

	CD3D11_TEXTURE2D_DESC desc;
	p_backBuffer->GetDesc (&desc);
	desc.Usage = D3D11_USAGE_STAGING;
	desc.BindFlags = NULL;
	desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
	ID3D11Texture2D *p_backBufferStaging = createTexture2D (desc);
	copyResource (p_backBufferStaging, p_backBuffer);
	downloadTexture2D (p_backBufferStaging, image);
	p_backBuffer->Release ();
}
